//
//  MappingExampleViewController.m
//  MappingExample
//

#import "MappingExampleViewController.h"

@implementation MappingExampleViewController

@synthesize startingLocation, lastPlottedLocation;

- (void)viewDidLoad {
    [super viewDidLoad];
	
	float viewHeight = 25.0;
	CGRect viewRect = [self.view bounds];
	
	CGRect addressViewRect = CGRectMake(viewRect.origin.x,
										viewRect.size.height - viewHeight,
										viewRect.size.width,
										viewHeight);
	
	addressView = [[UITextView alloc] initWithFrame:addressViewRect];
	addressView.backgroundColor = [UIColor blackColor];
	addressView.textColor = [UIColor whiteColor];
	addressView.textAlignment = UITextAlignmentCenter;
	
	CGRect distanceViewRect = CGRectMake(viewRect.origin.x,
										 viewRect.origin.y,
										 viewRect.size.width-50.0,
										 viewHeight);
	
	distanceView = [[UITextView alloc] initWithFrame:distanceViewRect];
	distanceView.backgroundColor = [UIColor blackColor];
	distanceView.textColor = [UIColor whiteColor];
	distanceView.textAlignment = UITextAlignmentCenter;
	
	CGRect headingViewRect = CGRectMake(viewRect.size.width-50.0,
										viewRect.origin.y,
										50.0,
										viewHeight);
	
	headingView = [[UITextView alloc] initWithFrame:headingViewRect];
	headingView.backgroundColor = [UIColor blackColor];
	headingView.textColor = [UIColor whiteColor];
	headingView.textAlignment = UITextAlignmentCenter;
	
	// 지도 위/아래에 공간 만든다.
	viewRect.size.height -= viewHeight * 2;
	viewRect.origin.y += viewHeight;
	
	// 지도 뷰를 생성하고 몇 가지 속성 설정
	map = [[MKMapView alloc] initWithFrame:viewRect];
	map.mapType = MKMapTypeSatellite;
	map.showsUserLocation = YES;
	map.delegate = self;

	// 위치 관리자 생성하고 몇 가지 속성 설정
	lm = [[CLLocationManager alloc] init];
	lm.desiredAccuracy = kCLLocationAccuracyBest; // 가장 정확한 값
	lm.distanceFilter = kCLDistanceFilterNone; // 모든 이동 상황 알림
	lm.delegate = self;
	
	// 위치 정보 업데이트 시작
	[lm startUpdatingLocation];
	
	// 나침반 센서가 있는지?
	if (CLLocationManager.headingAvailable) {
		[lm startUpdatingHeading];
	}
	
	// 뷰를 모두 본문에 붙인다.
	[self.view addSubview:addressView];
	[self.view addSubview:map];
	[self.view addSubview:distanceView];
	[self.view addSubview:headingView];
}

- (void)updateMapViewWithLocation:(CLLocation *)location shouldZoom:(BOOL)doZoom {
	
	// 업데이트가 자주 되도록 많이 확대
	float zoomLevel = 0.0005;
	
	// 화면에 지도가 그려지지 않았다면 배율을 설정해야 하지만, 그린 적이 있다면 배율을 설정하지 않는다.
	// 사용자가 배율을 변경한 경우에 화면이 다시 원래 배율로 돌아가지 않도록 막아준다.
	if (doZoom) {
		MKCoordinateRegion region = MKCoordinateRegionMake(location.coordinate,
														   MKCoordinateSpanMake(zoomLevel,
																				zoomLevel));
		[map setRegion:[map regionThatFits:region] animated:TRUE];
	}
	else {
		[map setCenterCoordinate:location.coordinate animated:YES];
	}
}

- (void)updateGeoCoderWithLocation:(CLLocation *)location {
	// 역 지오코딩
	if (geo) {
		[geo cancel];
	}
	
	geo = [[MKReverseGeocoder alloc] initWithCoordinate:location.coordinate];
	geo.delegate = self;
	[geo start];
}

- (void)updateHeadingView {
	NSString *headingText;
	if (previousHeading < 45.0) {
		headingText = @"N";
	}
	if (previousHeading >= 45.0 && previousHeading < 90.0) {
		headingText = @"NE";
	}
	if (previousHeading >= 90.0 && previousHeading < 135.0) {
		headingText = @"E";
	}
	if (previousHeading >= 135.0 && previousHeading < 180.0) {
		headingText = @"SE";
	}
	if (previousHeading >= 180.0 && previousHeading < 225.0) {
		headingText = @"S";
	}
	if (previousHeading >= 225.0 && previousHeading < 270.0) {
		headingText = @"SW";
	}
	if (previousHeading >= 270.0 && previousHeading < 315.0) {
		headingText = @"W";
	}
	if (previousHeading >= 315.0) {
		headingText = @"NW";
	}
	headingView.text = headingText;
}

- (void)plotLocation:(CLLocation *)loc {
	CLLocationCoordinate2D route[2];
	
	route[0] = lastPlottedLocation.coordinate;
	route[1] = loc.coordinate;
	
	MKPolyline *routeLine = [MKPolyline polylineWithCoordinates:route count:2];
	[map addOverlay:routeLine];
	[routeLine release];
	
	self.lastPlottedLocation = loc;
}

- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder
	   didFindPlacemark:(MKPlacemark *)placemark {
	NSString *street = [placemark.addressDictionary objectForKey:@"Street"];
	NSString *city = [placemark.addressDictionary objectForKey:@"City"];
	addressView.text = [NSString stringWithFormat:@"%@, %@", street, city];
}

- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFailWithError:(NSError *)error {
	[geo cancel];
}

- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay {
	MKPolylineView *plView = [[MKPolylineView alloc] initWithOverlay:overlay];
	plView.strokeColor = [UIColor redColor];
	plView.lineWidth = 3.0;
	
	return [plView autorelease];
}

- (void)locationManager:(CLLocationManager *)manager
	didUpdateToLocation:(CLLocation *)newLocation
		   fromLocation:(CLLocation *)oldLocation {
	
	NSTimeInterval eventAge = [newLocation.timestamp timeIntervalSinceNow];
	
	// 캐시된 위치 정보를 무시할 목적으로 5초가 지난 값은 모두 무시한다.
	if (abs(eventAge) < 5) {
		
		// 정확도가 10m 이내인지 확인한다.
		if ([newLocation horizontalAccuracy] > 0.0f
			&& [newLocation horizontalAccuracy] <= 10.0f) {
			
			// 첫번째 위치 정보인가?
			if (!startingLocation) {
				self.startingLocation = newLocation;
				self.lastPlottedLocation = newLocation;
				[self updateMapViewWithLocation:newLocation shouldZoom:YES];
			}
			else {
				[self updateMapViewWithLocation:newLocation shouldZoom:NO];
			}
			
			// 위치가 변경됐다면, 주소와 거리 뷰에 새로운 내용을 채워 넣는다.
			if (newLocation.coordinate.latitude != oldLocation.coordinate.latitude
				|| newLocation.coordinate.longitude != oldLocation.coordinate.longitude) {
				[self updateGeoCoderWithLocation:newLocation];
				distanceView.text = [NSString
									 stringWithFormat:@"Distance travelled from start: %3.1fm",
									 [newLocation distanceFromLocation:self.startingLocation]];
				[self plotLocation:newLocation];
			}
		}
	}
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
	[lm stopUpdatingLocation];
	if (CLLocationManager.headingAvailable) {
		[lm stopUpdatingHeading];
	}
	
	NSLog(@"an error occured: %@", error);
}

- (void)locationManager:(CLLocationManager *)manager
	   didUpdateHeading:(CLHeading *)newHeading {
	if (newHeading.trueHeading != previousHeading) {
		previousHeading = newHeading.trueHeading;
		[self updateHeadingView];
	}
}

- (void)dealloc {
	[map release];
	
	[lm stopUpdatingLocation];
	[lm release];
	
	[geo release];

	[addressView release];
	[distanceView release];
	[startingLocation release];
	[lastPlottedLocation release];
	
    [super dealloc];
}

@end